package com.spice.service.entity.job;

import com.spice.common.util.DateUtil;
import com.spice.service.entity.enums.JobStatusEnum;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * 运行工作的父类对象
 *
 * @author yhm
 * @date 2022/02/02
 */
@Data
@NoArgsConstructor
public class Job implements Comparable<Job> {

    /**
     * 工作完成量
     */
    protected long finished = 0;
    /**
     * 工作总量
     */
    protected long totalWork = Long.MAX_VALUE;
    /**
     * 当前状态
     */
    protected JobStatusEnum status = JobStatusEnum.NULL;
    /**
     * 工作创建时间戳，用于比较执行先后顺序
     */
    protected Long startTimeStamp = 0L;
    /**
     * 工作结束时间戳，用于计算运行时间
     */
    protected Long endTimeStamp = 0L;
    /**
     * 运行时间
     */
    protected Long timeCost = 0L;
    /**
     * 工作id
     */
    protected String id;
    /**
     * 创建时间
     */
    protected String createTime;
    /**
     * 优先级1~9，值越低，优先级越高。
     */
    protected Integer priority = 5;
    /**
     * 结果
     */
    protected HashMap<String, Object> data = null;

    protected Job data(String key, Object value) {
        if (data == null) {
            data = new HashMap<>();
        }
        this.data.put(key, value);
        return this;
    }

    {
        Date date = new Date();
        startTimeStamp = date.getTime();
        id = getClass().getName().substring(getClass().getName().lastIndexOf(".") + 1)
                + "_"
                + startTimeStamp
                + "_"
                + UUID.randomUUID().toString().substring(0, 4);
        createTime = DateUtil.getNowDateTime(date);
    }

    /**
     * 初始化方法（一般需要重写）
     */
    protected void initializeMethod() throws Exception {
        // doNothing
    }

    /**
     * 初始化错误方法（可选重写）
     */
    protected void initializeErrorMethod(Exception e) {
        this.status = JobStatusEnum.INIT_FAIL;
        e.printStackTrace();
        throw new RuntimeException("Job初始化出错");
    }

    /**
     * 外部调用初始化
     */
    public final void initialize() {
        new Thread(() -> {
            if (status.isNull()) {
                status = JobStatusEnum.INITIALIZING;
                try {
                    initializeMethod();
                } catch (Exception e) {
                    initializeErrorMethod(e);
                }
                status = JobStatusEnum.READY;
            } else {
                throw new RuntimeException("Job正在初始化或已经完成初始化了");
            }
        }).start();
    }

    /**
     * 运行方法（一般需要重写）
     */
    protected Boolean runMethod() throws Exception {
        // doNothing
        return true;
    }

    /**
     * job运行成功后运行的方法
     * 可选重写
     */
    protected void successMethod() {
        endTimeStamp = new Date().getTime();
        timeCost = endTimeStamp - startTimeStamp;
        status = JobStatusEnum.SUCCESS;
    }

    /**
     * job运行失败后运行的方法
     * 可选重写
     */
    protected void failureMethod() {
        endTimeStamp = new Date().getTime();
        timeCost = endTimeStamp - startTimeStamp;
        status = JobStatusEnum.FAILURE;
    }

    /**
     * job运行出错后运行的方法
     * 可选重写
     */
    protected void ErrorMethod(Exception e) {
        endTimeStamp = new Date().getTime();
        timeCost = endTimeStamp - startTimeStamp;
        status = JobStatusEnum.FAILURE;
        e.printStackTrace();
        throw new RuntimeException("Job运行出错");
    }

    public final void run() {
        new Thread(() -> {
            if (status.isReady()) {
                status = JobStatusEnum.RUNNING;
                try {
                    if (runMethod()) {
                        successMethod();
                    } else {
                        failureMethod();
                    }
                } catch (Exception e) {
                    ErrorMethod(e);
                }
            } else {
                throw new RuntimeException("Job尚未完成初始化");
            }
        }).start();
    }

    protected void finishPercent(double percent) {
        finished += totalWork * (percent / 100);
    }

    public boolean isReady() {
        return this.status.isReady();
    }

    public boolean isDone() {
        return this.status.isDone();
    }

    public boolean isSuccess() {
        return this.status.isSuccess();
    }

    public double getFinishedRate() {
        if (100.0 * finished / totalWork > 99.9){
            return 100.0;
        } else {
            return (double) Math.round(100.0 * finished / totalWork) / 100;
        }
    }

    /**
     * 用于比较执行先后顺序
     *
     * @param o
     * @return
     */
    @Override
    public int compareTo(Job o) {
        if (this.priority.equals(o.priority)) {
            return this.startTimeStamp.compareTo(o.startTimeStamp);
        } else {
            return this.priority.compareTo(this.priority);
        }
    }

}
